package com.mazaiting.manager.runner;

import com.mazaiting.common.core.factory.NamedThreadFactory;
import com.mazaiting.common.core.utils.DateTimeUtil;
import com.mazaiting.manager.listeners.event.RuleRelationEvent;
import com.mazaiting.scheduler.runner.JobRunner;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 初始化 Redis 中的数据
 *
 * @author mazaiting
 * @create_time 2022/9/5 12:14
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class InitializeRedisDataRunner implements ApplicationRunner {

    private final ThreadPoolExecutor webDtpExecutor;
    private final ApplicationEventPublisher publisher;
    private final JobRunner jobRunner;

    private final HikariDataSource hikariDataSource;
    /**
     * 远程 java 扩展
     */
    private HikariPoolMXBean hikariPoolMXBean;

    private final Timer timer = new Timer();

    @Override
    public void run(ApplicationArguments args) {
        webDtpExecutor.submit(() -> {
            long start = System.currentTimeMillis();
            log.info("=======初始化权限角色信息开始: start - {} =======", DateTimeUtil.time2String(start, DateTimeUtil.YYYY_MM_DD_HH_MM_SS_SSS));
            publisher.publishEvent(new RuleRelationEvent());
            long end = System.currentTimeMillis();
            log.info("=======初始化权限角色信息结束: end   - {} =======", DateTimeUtil.time2String(end, DateTimeUtil.YYYY_MM_DD_HH_MM_SS_SSS));
            log.info("=======初始化权限角色信息共计耗时: {} ms =======", end - start);
        });
        webDtpExecutor.submit(() -> {
            long start = System.currentTimeMillis();
            log.info("=======初始化定时任务信息开始: start - {} =======", DateTimeUtil.time2String(start, DateTimeUtil.YYYY_MM_DD_HH_MM_SS_SSS));
            jobRunner.initialize();
            long end = System.currentTimeMillis();
            log.info("=======初始化定时任务信息结束: end   - {} =======", DateTimeUtil.time2String(end, DateTimeUtil.YYYY_MM_DD_HH_MM_SS_SSS));
            log.info("=======初始化定时任务信息共计耗时: {} ms =======", end - start);
        });
        printThread();
        printDatasource();
    }

    /**
     * 监控数据源
     *
     * @see com.zaxxer.hikari.pool.HikariPool#logPoolState
     */
    private void printDatasource() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    // 池名称
                    String poolName = hikariDataSource.getPoolName();
                    if (Objects.isNull(hikariPoolMXBean)) {
                        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
                        ObjectName objectPoolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + hikariDataSource.getPoolName() + ")");
                        hikariPoolMXBean = JMX.newMXBeanProxy(mBeanServer, objectPoolName, HikariPoolMXBean.class);
                    }
                    // 配置的最大总连接数
                    int maximumPoolSize = hikariDataSource.getMaximumPoolSize();
                    // 总连接
                    int totalConnections = hikariPoolMXBean.getTotalConnections();
                    // 活跃连接
                    int activeConnections = hikariPoolMXBean.getActiveConnections();
                    // 空闲连接
                    int idleConnections = hikariPoolMXBean.getIdleConnections();
                    // 等待连接
                    int threadsAwaitingConnection = hikariPoolMXBean.getThreadsAwaitingConnection();
                    log.info("{} - status (maxTotal={}, total={}, active={}, idle={}, waiting={})",
                            poolName, maximumPoolSize, totalConnections, activeConnections, idleConnections, threadsAwaitingConnection);
                } catch (Throwable cause) {
                    log.error("获取连接池数据失败: ", cause);
                }
            }
            // 需要延时 1s, 否则数据库未连接上会报空
        }, 1000, 60 * 1000);
    }

    /**
     * 监控线程
     * 线程自动扩展与收缩
     */
    private void printThread() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                // 获取线程数
                int activeCount = webDtpExecutor.getActiveCount();
                // 核心线程数
                int corePoolSize = webDtpExecutor.getCorePoolSize();
                // 池中最大线程数
                int largestPoolSize = webDtpExecutor.getLargestPoolSize();
                // 允许最大线程数
                int maximumPoolSize = webDtpExecutor.getMaximumPoolSize();
                // 线程池工厂
                ThreadFactory threadFactory = webDtpExecutor.getThreadFactory();
                // 线程名
                String name = "pool";
                if (threadFactory instanceof NamedThreadFactory namedThreadFactory) {
                    name = namedThreadFactory.getNamePrefix().split("-")[0];
                }

                log.info("线程名: {}, 允许最大线程数: {}, 池中最大线程数: {}, 核心线程数: {}, 活跃线程数: {}",
                        name, maximumPoolSize, largestPoolSize, corePoolSize, activeCount);
                // 如果小于 2/3, 则设置为 2/3, 否则设置为 2 倍
                if (largestPoolSize < maximumPoolSize / 2) {
                    maximumPoolSize = maximumPoolSize / 3 * 2;
                    if (maximumPoolSize > corePoolSize) {
                        webDtpExecutor.setMaximumPoolSize(maximumPoolSize);
                    }
                } else {
                    webDtpExecutor.setMaximumPoolSize(maximumPoolSize * 2);
                }
            }
        }, 0, 60 * 1000);

    }
}
